home *** CD-ROM | disk | FTP | other *** search
/ Network PC / Network PC.iso / amiga utilities / communication / internet / amitcp3.0b / src.lha / src / util / ls / print.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-09-08  |  14.1 KB  |  588 lines

  1. RCS_ID_C "$Id: print.c,v 1.12 1993/11/12 06:03:26 ppessi Exp $";
  2. /*
  3.  * print.c --- print a directory listing
  4.  *
  5.  * Author: ppessi <Pekka.Pessi@hut.fi>
  6.  *
  7.  * Copyright (c) 1991, 1993 Pekka Pessi. All rights reserved
  8.  *
  9.  * Last modified: Fri Nov 12 08:03:02 1993 ppessi
  10.  *
  11.  */
  12.  
  13. #include <stdlib.h>
  14. #include <string.h>
  15. #include <dos/dosasl.h>
  16. #include <dos/dosextens.h>
  17. #include <dos/datetime.h>
  18. #include <clib/utility_protos.h>
  19.  
  20. #include <exec/io.h>
  21. #include <exec/memory.h>
  22. #include <devices/conunit.h>
  23.  
  24. #ifdef HAVE_MULTIUSER
  25. #include <libraries/multiuser.h>
  26. #else
  27. /* Only thing we need to know is SUID bit */
  28. #define muFIBB_SET_UID        (31)  /* Change owner during execution */
  29. #define muFIBF_SET_UID        (1<<muFIBB_SET_UID)
  30. #endif
  31.  
  32. #include "ls.h"
  33.  
  34. #define FUDGE 2            /* space between columns */
  35. #define DEFAULTWIDTH     77    /* -C width */
  36.  
  37. #ifndef FIBB_OTR_READ
  38. /* 
  39.  * These bits are undefined before DOS 3.0 
  40.  * FIBB are bit definitions, FIBF are field definitions 
  41.  * Regular RWED bits are 0 == allowed. 
  42.  * NOTE: GRP and OTR RWED permissions are 0 == not allowed! 
  43.  */
  44. #define FIBB_OTR_READ      15   /* Other: file is readable */
  45. #define FIBB_OTR_WRITE     14   /* Other: file is writable */
  46. #define FIBB_OTR_EXECUTE   13   /* Other: file is executable */
  47. #define FIBB_OTR_DELETE    12   /* Other: prevent file from being deleted */
  48. #define FIBB_GRP_READ      11   /* Group: file is readable */
  49. #define FIBB_GRP_WRITE     10   /* Group: file is writable */
  50. #define FIBB_GRP_EXECUTE   9    /* Group: file is executable */
  51. #define FIBB_GRP_DELETE    8    /* Group: prevent file from being deleted */
  52. #endif
  53.  
  54. /*
  55.  * checkbreak
  56.  *      exit if CTRL-C signal received
  57.  */ 
  58. static void
  59. checkbreak(void)
  60. {
  61.   if (CheckSignal(SIGBREAKF_CTRL_C)) {
  62.     FPuts(Stderr, "***Break\n");
  63.     exit(RETURN_FAIL);
  64.   }
  65. }
  66.  
  67. /*
  68.  * getconwidth
  69.  *      Try to find out the width of console window
  70.  */
  71. static int 
  72. getconwidth(BPTR out) 
  73. {
  74.   int width;
  75.   struct InfoData *id;
  76.   struct MsgPort *conTask;
  77.   
  78.   if (!out || !IsInteractive(out)) {
  79.     return 0;
  80.   }
  81.   conTask = ((struct FileHandle *)BADDR(out)) -> fh_Type;
  82.   if ((long) conTask < 0)
  83.     return 0;    
  84.   id = (struct InfoData *) AllocMem(sizeof(struct InfoData), MEMF_CLEAR);
  85.   if (!id) 
  86.     return 0;
  87.  
  88.   if (DoPkt(conTask, ACTION_DISK_INFO, MKBADDR(id), NULL, NULL, NULL, NULL)) {
  89. #if 0
  90.     struct ConUnit * conu = (id->id_InUse);
  91.     FPrintf(err, "Window id: %lx\n", (long) (id->id_VolumeNode));
  92.     FPrintf(err, "Console Unit: %lx\n", (long) (id->id_InUse));
  93.     FPrintf(err, "Window Size: X %ld Y %ld \n", conu->cu_XMax, conu->cu_YMax);
  94. #endif
  95.     width = ((struct ConUnit *)
  96.          ( ((struct IOStdReq *)(id->id_InUse))->io_Unit ))->cu_XMax;
  97.   } else { 
  98.     width = 0;
  99.   }
  100.   FreeMem(id, sizeof(*id));
  101.   return width;
  102. }
  103.  
  104. /*
  105.  * count_rows
  106.  *      calculate minimum amount of row the short listing fits
  107.  */
  108. static int
  109. count_rows(const UBYTE *widths,
  110.        UBYTE * col_widths,
  111.        int entries,
  112.        int width,
  113.        int fudge)        /* fudge factor */
  114. {
  115.   int col, row, rows, row_width;
  116.   UBYTE * col_width;
  117.  
  118.   for (rows = 1; rows < entries; rows ++) {
  119.     row_width = 0;
  120.     col_width = col_widths;
  121.     for (col = 0; col < entries; col += rows, row_width += fudge) {
  122.       int w = 0;
  123.       for (row = 0; row < rows && col + row < entries; row ++)
  124.         w = max(w, widths[col + row]);
  125.       row_width += w;
  126.       *col_width++ = w;
  127.       if (w > width)
  128.     return entries;
  129.       if (row_width > width)
  130.         break;
  131.     }
  132.     if (row_width <= width)
  133.       break;
  134.   }
  135.   return rows;
  136. }
  137.  
  138. /* 
  139.  * type_char
  140.  *      give appropriate prefix char for -F
  141.  */
  142. INLINE static char
  143. type_char(const struct ExAllData * slot)
  144. {
  145.   char c = '\0';
  146.   switch (slot->ed_Type) {
  147.   case ST_ROOT:
  148.   case ST_USERDIR:
  149.     c = '/'; break;
  150.   case ST_SOFTLINK:
  151.   case ST_LINKDIR:
  152.     c = '@'; break;
  153.   case ST_LINKFILE:
  154.     c = '#'; break;
  155.   }
  156.   return c;
  157. }
  158.  
  159. /* 
  160.  * total
  161.  *    print total size of a directory 
  162.  */
  163. INLINE static void
  164. total(const struct ExAllData ** slots, 
  165.       int entries)
  166. {
  167.   /* Normally, each entry takes a block */
  168.   short i;
  169.   long used_blocks = 0;
  170.  
  171.   for (i = 0; i < entries; i++) {
  172.     /* Correct for small FFS files only  */
  173.     used_blocks += slots[i]->ed_Blocks;
  174.   }
  175.   /* Print only # of used blocks */
  176.   FPrintf( Stdout, "total %ld\n", used_blocks + 1 >> 1);
  177. }
  178.  
  179. /* ST_PIPEFILE (-5) -> "p" for pipes 
  180.  * ST_LINKFILE (-4) -> "h" for hard link to file 
  181.  * ST_FILE     (-3) -> "-" for plain files
  182.  * ST_ROOT       1  -> "r" for root directory
  183.  * ST_USERDIR     2  -> "d" for ordinary directory
  184.  * ST_SOFTLINK     3  -> "l" for soft link
  185.  * ST_LINKDIR     4  -> "D" for hard link to dir
  186.  */
  187. static char f[10] = "ph-210rdlD";
  188. static char r[2] = "-r";
  189. static char w[8] = "-D-DWwWw";
  190. static char x[8] = "-xTtSsSs";
  191. static char pchars[11];
  192.  
  193. /*
  194.  * getprot
  195.  *      get a Unix style protection bit representation 
  196.  */
  197. static UBYTE *
  198. getprot(BYTE type, LONG pbits, UWORD owner)
  199. {
  200.   UBYTE *p = pchars;
  201.  
  202.   if (type < ST_PIPEFILE)
  203.     type = ST_PIPEFILE;
  204.   else if (type > ST_LINKDIR)
  205.     type = ST_LINKDIR;
  206.  
  207.   type -= ST_PIPEFILE;
  208.  
  209.   /* Invert lowest 4 bits */
  210.   pbits ^= 0x0f;
  211.  
  212.   /* Is the file owned by Mr. Nobody? */
  213.   if (owner == 0) {
  214.     ULONG bits = pbits & (FIBF_READ | FIBF_DELETE | FIBF_WRITE | FIBF_EXECUTE);
  215.     pbits |=  bits << FIBB_GRP_DELETE | bits << FIBB_OTR_DELETE ; 
  216.   }
  217.  
  218.   *p++ = f[type];
  219.   *p++ = r[1 & (pbits >> FIBB_READ)];
  220.   *p++ = w[7 & (pbits >> FIBB_DELETE)];
  221.   *p++ = x[(1 & (pbits >> FIBB_EXECUTE)) + 
  222.        (4 & (pbits >> muFIBB_SET_UID - 2))];
  223.   *p++ = r[1 & (pbits >> FIBB_GRP_READ)];
  224.   *p++ = w[7 & (pbits >> FIBB_GRP_DELETE)];
  225.   *p++ = x[1 & (pbits >> FIBB_GRP_EXECUTE)];
  226.   *p++ = r[1 & (pbits >> FIBB_OTR_READ)];
  227.   *p++ = w[7 & (pbits >> FIBB_OTR_DELETE)];
  228.   *p++ = x[(1 & (pbits >> FIBB_OTR_EXECUTE)) +
  229.        (2 & (pbits >> FIBB_PURE - 1)) + /* seen as 'sticky' */
  230.        (4 & (pbits >> FIBB_SCRIPT - 2))];
  231.   *p++ = '\0';
  232.   return pchars;
  233. }
  234.  
  235. static int current_year = 0;
  236. static UBYTE times[16];
  237.  
  238. /*
  239.  * lsdate
  240.  *      convert date into ls format
  241.  */
  242. static UBYTE *
  243. lsdate(const struct ExAllData * slot)
  244. {
  245.   int year;
  246.   UBYTE dates[LEN_DATSTRING];
  247.  
  248. #if __SASC == 0
  249.   struct DateTime datetime =
  250.     { { 0, 0, 0}, 0, 0, NULL, dates, times + 7};
  251. #else
  252.   struct DateTime datetime =
  253.     { { 0, 0, 0}, 0, 0, NULL, NULL, NULL};
  254.  
  255.   datetime.dat_StrDate = dates;
  256.   datetime.dat_StrTime = times + 7;
  257. #endif
  258.   if (!current_year) {
  259.     (void) DateStamp(&datetime.dat_Stamp);
  260.     (void) DateToStr(&datetime);
  261.     current_year = 1900 + (dates[7] - '0') * 10 + dates[8] - '0';
  262.   }
  263.  
  264.   datetime.dat_Stamp.ds_Days   = slot->ed_Days;
  265.   datetime.dat_Stamp.ds_Minute = slot->ed_Mins;
  266.   datetime.dat_Stamp.ds_Tick   = slot->ed_Ticks;
  267.   (void) DateToStr(&datetime);
  268.  
  269.   /* if file's year is not current, print it instead of time */
  270.   year = 1900 + (dates[7] - '0') * 10 + dates[8] - '0';
  271.   if (year != current_year) {
  272.     times[7] = '1';
  273.     times[8] = '9';
  274.     times[9] = dates[7];
  275.     times[10] = dates[8];
  276.     times[11] = ' ';
  277.   }
  278.  
  279.   times[12] = '\0';        /* don't show seconds */
  280.  
  281.   /* Hack date field for printing */
  282.   times[0] = dates[3];
  283.   times[1] = dates[4];
  284.   times[2] = dates[5];
  285.   times[3] = ' ';
  286.   if (dates[0] == '0')
  287.     times[4] = ' ';
  288.   else
  289.     times[4] = dates[0];
  290.   times[5] = dates[1];
  291.   times[6] = ' ';
  292.  
  293.   return times;
  294. }
  295.  
  296. /*
  297.  * printlink
  298.  *      print out the soft link
  299.  */
  300. static void
  301. printlink(const char * path, 
  302.       const struct ExAllData * slot)
  303. {
  304.   static char *buffer = NULL;
  305.   BPTR lock; 
  306.   struct FileLock *fl;
  307.  
  308.   if (!buffer) {
  309.     buffer = malloc(MAXPATHLEN);
  310.     if (!buffer) return;
  311.   }
  312.  
  313.   lock = Lock(path, SHARED_LOCK); 
  314.   if (fl = (struct FileLock *)(lock << 2)) {
  315.     if (ReadLink(fl->fl_Task, lock, slot->ed_Name, buffer, MAXPATHLEN))
  316.       FPrintf(Stdout, " -> %s", buffer);
  317.     UnLock(lock);
  318.   }
  319. }
  320.  
  321. /*
  322.  * shortprint
  323.  *      list a directory in short format
  324.  */
  325. static void
  326. shortprint(const struct ExAllData ** slots,
  327.        int entries,
  328.        struct options options)
  329. {
  330.   int width;
  331.   int rows, columns;
  332.   int i, j;
  333.   UBYTE * widths = malloc(entries + 1);
  334.   UBYTE * col_widths = malloc(entries + 1);
  335.   if (!widths || !col_widths)
  336.     exit(RETURN_FAIL);
  337.  
  338.   if (options.singlecolumn)
  339.     width = 0;
  340.   else {
  341.     width = getconwidth(Stdout);
  342.     /* unknown width, but forced multicolumn */
  343.     if (width == 0 && options.multicolumn)
  344.       width = DEFAULTWIDTH;
  345.   }
  346.  
  347.   if (width > FUDGE + 1) {
  348.     for(i = 0; i < entries; i++) {
  349.       widths[i] = strlen(slots[i]->ed_Name);
  350.       if (options.kilos)
  351.     widths[i] += 5;        /* no files longer than 9999 kilos ;) */
  352.       if (options.inode)
  353.     widths[i] += 7;        /* actually no key blocks > 4194303 */
  354.       if (options.filetype && type_char(slots[i]))
  355.     widths[i]++;
  356.     }
  357.     rows = count_rows(widths, col_widths, entries, width, FUDGE);
  358.     columns = entries / (rows + 1) + 1;
  359.   } else {
  360.     rows = entries;
  361.     columns = 1;
  362.   }
  363.  
  364.   if (options.kilos)
  365.     total(slots, entries);
  366.  
  367.   for (i = 0; i < rows; i ++) {
  368.     UBYTE * col_width = col_widths;
  369.  
  370.     checkbreak();
  371.  
  372.     for (j = i; j < entries; j += rows ) {
  373.       char filetype;
  374.       if (options.inode)
  375.     FPrintf(Stdout, "%6ld ", slots[j]->ed_Key);
  376.       /* if -s is wanted */
  377.       if (options.kilos)
  378.     FPrintf(Stdout, "%4ld ", slots[j]->ed_Blocks + 1 >> 1);
  379.       FPuts(Stdout, (char *)(slots[j]->ed_Name));
  380.       if (options.filetype && (filetype = type_char(slots[j])))
  381.       FPutC(Stdout, filetype);
  382.  
  383.       if (j + rows < entries) { /* tabulate */
  384.         FWrite(Stdout,
  385.            "                                        "
  386.            "                                        ",
  387.            /* 80 spaces, max filename lenght == 80 */
  388.            sizeof(char), *col_width++ - widths[j] + FUDGE);
  389.       } else {
  390.     FPutC(Stdout, '\n');
  391.       }
  392.     }
  393.   }
  394.   free(widths);
  395.   free(col_widths);
  396. }
  397.  
  398. /*
  399.  * longprint
  400.  *      list directory in long format
  401.  */
  402. static void
  403. longprint(const char * path, const struct ExAllData ** slots,
  404.       int entries, struct options options)
  405. {
  406.   int i;
  407.   char filetype;
  408.  
  409.   total(slots, entries);
  410.  
  411.   for (i = 0; i < entries; i++) {
  412.     long va_list[10];
  413.     const struct ExAllData * slot = slots[i];
  414.  
  415.     checkbreak();
  416.  
  417.     /* if -i is wanted */
  418.     if (options.inode)
  419.       FPrintf(Stdout, "%6ld ", slot->ed_Key);
  420.     /* if -s is wanted */
  421.     if (options.kilos)
  422.       FPrintf(Stdout, "%4ld ", slot->ed_Blocks + 1 >> 1);
  423.  
  424.     /* normal -l listing */
  425.     va_list[0] = (long)getprot(slot->ed_Type, slot->ed_Prot, slot->ed_OwnerUID);
  426.     va_list[1] = 1;
  427.     va_list[2] = (long)user(slot->ed_OwnerUID);
  428.     if (options.group) 
  429.       va_list[3] = (long)group(slot->ed_OwnerGID);
  430.     else
  431.       va_list[3] = (long)"";
  432.     va_list[4] = slot->ed_Size;
  433.     /* This is a kludge */
  434.     va_list[5] = (long)lsdate(slot);
  435.     va_list[6] = (long)(options.pathname ? path : "");
  436.     va_list[7] = (long)slot->ed_Name;
  437.     VFPrintf(Stdout, options.group ? "%s %2ld %s %s %8ld %s %s%s" :
  438.                  "%s %2ld %s %s%8ld %s %s%s", va_list);
  439.  
  440.     if (options.filetype && (filetype = type_char(slot)))
  441.       FPutC(Stdout, filetype);
  442.     if (!options.symbolic && (slot->ed_Type == ST_SOFTLINK)) {
  443.       printlink(path, slot);
  444.     }
  445.     FPutC(Stdout, '\n');
  446.   }
  447. }
  448.  
  449. static int
  450. compare_name(const struct ExAllData * p, const struct ExAllData * v)
  451. {
  452.   return Stricmp(p->ed_Name, v->ed_Name);
  453. }
  454.  
  455. static int
  456. compare_name_rev(const struct ExAllData * p, const struct ExAllData * v)
  457. {
  458.   /* p and v reversed... */
  459.   return Stricmp(v->ed_Name, p->ed_Name);
  460. }
  461.  
  462. static int
  463. compare_time(const struct ExAllData * p, const struct ExAllData * v)
  464. {
  465.   return CompareDates((struct DateStamp*)&(p->ed_Days),
  466.               (struct DateStamp*)&(v->ed_Days));
  467. }
  468.  
  469. static int
  470. compare_time_rev(const struct ExAllData * p, const struct ExAllData * v)
  471. {
  472.   /* p and v reversed... */
  473.   return CompareDates((struct DateStamp*)&(v->ed_Days),
  474.               (struct DateStamp*)&(p->ed_Days));
  475. }
  476.  
  477. static short
  478. complete(char * path)
  479. {
  480.   short pathlen = strlen(path);
  481.   /* add directory separator to path, if none */
  482.   if (pathlen > MAXPATHLEN - MAXFILENAMELEN - 1)
  483.     return -1;
  484.   if (pathlen > 0 && path[pathlen - 1] != ':' && path[pathlen - 1] != '/') {
  485.     path[pathlen++] = '/'; path[pathlen] = '\0';
  486.   }
  487.   return pathlen;
  488. }
  489.  
  490. void
  491. doprint(char * path,
  492.     const struct ExAllList *buffers,
  493.     struct options options)
  494. {
  495.   int entry = 0, dentry = 0, i, di;
  496.   int entries = buffers->El_Number;
  497.   struct ExAllData **dslots, **slots;
  498.   short pathlen;
  499.  
  500.   checkbreak();
  501.  
  502.   if (!entries) {
  503.     FPuts(Stdout, "Volume or directory is empty.\n");
  504.     return;
  505.   }
  506.   slots = malloc((entries + 1) * sizeof(char *));
  507.   if (!slots) {
  508.     exit(RETURN_FAIL);
  509.   }
  510.  
  511.   if ((pathlen = complete(path)) < 0) {
  512.     /* pathname overflow - break... */
  513.     FPrintf(Stderr, "%s: too long pathname\n", path);
  514.     return;
  515.   }
  516.  
  517.   for (; buffers; buffers = buffers->El_Next) {
  518.     struct ExAllData *ptr;
  519.     for (ptr = &(buffers->El_Data); ptr; ptr = ptr->ed_Next) {
  520.       /* Add entry into table */
  521.       dentry += (ptr->ed_Type > 0);
  522.       slots[entry++] = ptr;
  523.       if (entry > entries) {
  524.         FPrintf(Stderr,
  525.         "ls: Fatal error, entry count "
  526.         "mismatch %ld != %ld\n", entries, entry);
  527.         exit(RETURN_FAIL);
  528.       }
  529.     }
  530.   }
  531.   if (!options.fast && entries > 1) {
  532.     int (*compare)(void *, void *) = NULL;
  533.  
  534.     if ( options.reverse ) {
  535.       compare = options.sort_time ? (compare_time_rev) : (compare_name_rev);
  536.     } else {
  537.       compare = options.sort_time ? (compare_time) : (compare_name);
  538.     }
  539.  
  540.     quick_sort((void **)slots, entries, compare);
  541.   }
  542.   dslots = malloc((dentry + 1) * sizeof(char *));
  543.  
  544.   if (!dslots)
  545.     exit(RETURN_FAIL);
  546.  
  547.   for (i = 0, di = 0, entry = 0; i < entries; i++) {
  548.     if (slots[i]->ed_Type > 0) {
  549.       dslots[di++] = slots[i];
  550.       if (!options.dir) continue;
  551.     }
  552.     slots[entry++] = slots[i];
  553.   }
  554.  
  555.   if (entry) {
  556.     if (options.longformat) {
  557.       longprint(path, slots, entry, options);
  558.     } else {
  559.       shortprint(slots, entry, options);
  560.     }
  561.   }
  562.  
  563.   free(slots);
  564.   if (!options.dir) {
  565.     static int donewline = 0;
  566.     struct ExAllList *listing;
  567.  
  568.     options.dir = !options.recursion;
  569.  
  570.     for (i = 0; i < dentry; i++) {
  571.       /* concatenate new entry to path name */
  572.       strncpy(path + pathlen, (char *)(dslots[i]->ed_Name), 
  573.           MAXPATHLEN - pathlen);
  574.       /* avoid newline at top */
  575.       if (donewline)
  576.           FPutC(Stdout, '\n');
  577.       if (dentry > 1 || donewline)
  578.     FPrintf(Stderr, "%s:\n", path[0] ? path : "\"\"" );
  579.       donewline = 1;
  580.       listing = listdir(path, options);
  581.       if (listing)
  582.     doprint(path, listing, options);
  583.       bfree(listing);
  584.     }
  585.   }
  586.   free(dslots);
  587. }
  588.